package com.bjpowernode.core;

import com.bjpowernode.constant.Constant;
import com.bjpowernode.util.FileUtils;
import com.bjpowernode.util.HttpUtils;
import com.bjpowernode.util.LogUtils;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.concurrent.*;


/**
 * 下载器
 */
public class Downloader {

    //线程池对象
     public static ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(Constant.THREAD_NUM,Constant.THREAD_NUM,0,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(Constant.THREAD_NUM));

    public static ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
    public static void download(String url) {
        //获取文件名
        String fileName = HttpUtils.getFileName(url);
        //文件的下载路径
        fileName = Constant.PATH + fileName;

        //获取本地文件大小
        long localFileSize = FileUtils.getFileSize(fileName);

        //获取链接对象
        HttpURLConnection httpURLConnection = null;
        try {
            httpURLConnection = HttpUtils.getHttpURLConnection(url);
            //获取下载文件大小
            long httpFileContentLength = httpURLConnection.getContentLengthLong();
            //判断是否已下载
            if (localFileSize >= httpFileContentLength) {
                LogUtils.info("{}文件已下载,无需重新下载", fileName);
                return;
            }

            //创建获取下载信息的任务对象
            DownloadInfoThread downloadInfoThread = new DownloadInfoThread(httpFileContentLength);
            //将任务交给线程,每一秒执行一次
            scheduledExecutorService.scheduleAtFixedRate(downloadInfoThread, 1, 1, java.util.concurrent.TimeUnit.SECONDS);

            //切分任务
            ArrayList<Future> list = new ArrayList<>();
            spilt(url, list);

            list.forEach(future -> {
                try {
                    //获取结果
                    future.get();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
            });

            //合并文件
            if (merge(fileName)){
                clear(fileName);
            }

        }catch (Exception e){
            LogUtils.error("下载失败");
        }
        finally {
            System.out.print("\r");
            System.out.print("下载完成");
            if (httpURLConnection != null) {
                //关闭链接对象
                httpURLConnection.disconnect();
            }

            //关闭
            scheduledExecutorService.shutdownNow();
            //关闭线程池
            poolExecutor.shutdown();
        }
    }

    /**
     * 分片下载
     * @param url
     * @param futureList
     */
    public static void spilt(String url, ArrayList<Future> futureList){
        //获取下载文件大小
        long contentLength = HttpUtils.getContentLength(url);

        //计算切分后文件大小
        long partSize = contentLength / Constant.THREAD_NUM;

        //计算分块个数
        for (int i = 0; i < Constant.THREAD_NUM; i++) {
            //计算起始位置
            long startPos = i * partSize;
            //计算结束位置
            long endPos;
            if (i == Constant.THREAD_NUM - 1) {
                //下载最后一块
                endPos = 0;
            } else {
                endPos = startPos + partSize;
            }
            //如果不是第一块起始位置加1
            if (i != 0) {
                startPos++;
            }

            //创建任务
            DownloaderTask downloaderTask = new DownloaderTask(url, i, startPos, endPos);

            //将任务提交给线程池,submit是Callable使用的
            Future<Boolean> future = poolExecutor.submit(downloaderTask);

            //将结果加入集合
            futureList.add(future);
        }
    }

    /**
     * 文件合并
     * @param fileName
     * @return
     */
    public static boolean merge(String fileName){
        LogUtils.info("开始合并文件{}", fileName);
        byte[] buffer = new byte[Constant.BYTE_SIZE];
        int len = -1;
        try(
                RandomAccessFile randomAccessFile = new RandomAccessFile(fileName, "rw");
                ){
            for (int i = 0; i < Constant.THREAD_NUM; i++) {
                try(BufferedInputStream bis = new BufferedInputStream((new FileInputStream(fileName + "_" +  i)))) {

                    while ((len = bis.read(buffer)) != -1) {
                        randomAccessFile.write(buffer, 0, len);
                    }
                }catch (Exception e){
                    LogUtils.error("合并文件{}失败", fileName);
                }
            }

            LogUtils.info("合并文件{}成功", fileName);
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 清空临时方法
     * @param fileName
     * @return
     */
    public static boolean clear(String fileName){
        for (int i = 0; i < Constant.THREAD_NUM; i++){
            File file = new File(fileName + "_" + i);
            file.delete();
        }
        return true;
    }
}
